package com.qixiucoding;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.JSONPath;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class JsonProcessor {
    public static final String RESULT = "#result#";
    private Map<String, Object> context = new HashMap<>();
    private JsonOperationFactory factory = new JsonOperationFactory();

    public String process(String inputJson, String configJson) {
        List<JSONObject> operations = JSON.parseArray(configJson, JSONObject.class);
        JSONObject inputObject = JSON.parseObject(inputJson);

        for (JSONObject operation : operations) {
            String opType = operation.getString("optype");
            JsonOperation jsonOperation = factory.createOperation(opType);
            jsonOperation.execute(inputObject, operation, context);
        }
        return context.get(RESULT).toString();
    }
}

interface JsonOperation {
    void execute(JSONObject input, JSONObject config, Map<String, Object> context);
}

class ResultjsonOperation implements JsonOperation {


    @Override
    public void execute(JSONObject input, JSONObject config, Map<String, Object> context) {
        String template = config.getString("template");
        JSONArray keys = config.getJSONArray("keys");
        if (keys != null && StrUtil.isNotBlank(template)) {
            for (int i = 0; i < keys.size(); i++) {
                String key = keys.getString(i);


                template = StrUtil.replace(template,"{" + key + "}", context.get(key) == null ? "null" : JSON.toJSONString(
                        context.get(key)));

            }
            context.put(JsonProcessor.RESULT, template);
        }
    }
}
class GetJsonOperation implements JsonOperation {
    @Override
    public void execute(JSONObject input, JSONObject config, Map<String, Object> context) {
        JSONObject op = config.getJSONObject("op");
        for (String key : op.keySet()) {
            JSONObject valueConfig = op.getJSONObject(key);
            String expression = valueConfig.getString("expression");
            String dataType = valueConfig.getString("datatype");

            Object value = JSONPath.eval(input, expression);
            if (null == value) {

            } else if ("string".equals(dataType)) {
                value = value.toString();
            } else if ("integer".equals(dataType)) {
                value = Integer.valueOf(value.toString());
            } else if ("boolean".equals(dataType)) {
                value = Boolean.valueOf(value.toString());
            } else if ("double".equals(dataType)) {
                value = Double.valueOf(value.toString());
            } else if ("long".equals(dataType)) {
                value = Long.valueOf(value.toString());
            } else if ("float".equals(dataType)) {
                value = Float.valueOf(value.toString());
            } else if ("array".equals(dataType)) {
                value = JSON.parseArray(JSON.toJSONString(value));
            } else if ("object".equals(dataType)) {
                value = JSON.parseObject(JSON.toJSONString(value.toString()));
            }
            context.put(key.replace("#", ""), value);
        }
    }
}

class CreateJsonOperation implements JsonOperation {
    @Override
    public void execute(JSONObject input, JSONObject config, Map<String, Object> context) {
        String type = config.getString("type");
        JSONObject op = config.getJSONObject("op");

        if ("jsonobject".equals(type)) {
            JSONObject result = new JSONObject();
            for (String key : op.keySet()) {
                if (StrUtil.isBlank(key)) {
                    continue;
                }
                JSONObject valueConfig = op.getJSONObject(key);
                String value = valueConfig.getString("value");
                String valueType = valueConfig.getString("valuetype");

                if (ObjectUtil.isNull(value)) {
                    result.put(key, value);
                }
                if (value.startsWith("#") && value.endsWith("#")) {
                    String contextKey = value.replace("#", "");
                    Object contextValue = context.get(contextKey);
                    result.put(key, contextValue);
                } else if ("string".equals(valueType)) {
                    result.put(key, value);
                } else if ("integer".equals(valueType)) {
                    result.put(key, Integer.valueOf(value));
                } else if ("boolean".equals(valueType)) {
                    result.put(key, Boolean.valueOf(value));
                } else if ("double".equals(valueType)) {
                    result.put(key, Double.valueOf(value));
                } else if ("long".equals(valueType)) {
                    result.put(key, Long.valueOf(value));
                } else if ("float".equals(valueType)) {
                    result.put(key, Float.valueOf(value));
                } else if ("array".equals(valueType)) {
                    result.put(key, JSON.parseArray(value));
                } else if ("object".equals(valueType)) {
                    result.put(key, JSON.parseObject(value));
                }

            }
            context.put(JsonProcessor.RESULT, result);
            context.putAll(result);
        }
    }
}

class JsonOperationFactory {
    public JsonOperation createOperation(String opType) {
        switch (opType) {
            case "getjson":
                return new GetJsonOperation();
            case "createjson":
                return new CreateJsonOperation();
            case "resultjson":
                return new ResultjsonOperation();
            default:
                throw new IllegalArgumentException("Unknown operation type: " + opType);
        }
    }
}